/**
 * 优化版观察者
 * @author Jeef
 * @tutorial 一般使用subcribe和emit就可以了
 */
export class JEventEmit<T = any> {

    /**观察触发方法数组 */
    private _subscribers: ((data?: T) => void)[] = []
    /** 排列顺序 */
    private _sortIDList: number[] = []
    /** 下一个id */
    private _nextID: number = 0
    /** id列表 */
    private _idList: number[] = []

    constructor(private _listenSubscribe?: () => void, private _listenUnsubscribe?: () => void) {

    }

    /**
     * 获取一个id
     */
    private _getNextID() {
        this._nextID++
        return this._nextID
    }

    /**
     * 添加订阅事件
     * @param func 订阅用的方法
     * @param sortID 排列用的id
     * @param id   事件的id
     * @param index 数组的位置
     */
    private _addSubscribe(func: (data?: T) => void, sortID: number, id: number, index?: number) {
        if (index == undefined) {
            this._sortIDList.push(sortID)
            this._subscribers.push(func)
            this._idList.push(id)
            return true
        }
        this._subscribers.splice(index, 0,
            func)
        this._sortIDList.splice(index, 0, sortID)
        this._idList.splice(index, 0, id)
        return true
    }

    /**
     * 删除订阅事件
     * @param num 序号
     */
    private _delSubscribe(num: number) {
        this._sortIDList.splice(num, 1)
        this._subscribers.splice(num, 1)
        this._idList.splice(num, 1)
    }

    /** 订阅观察者事件 
     * @param obj 订阅的事件
     * @param sortID 排列顺序,默认为1,可以为小数,根据数值不同,越大放在越后,越小放在越前,正数同值往后推,负数同值往前推
     * @returns 返回订阅的id,用来unsubscribe
     */
    subscribe(obj: (data?: T) => void, sortID?: number) {
        /** 触发监听订阅的方法 */
        if (this._listenSubscribe)
            this._listenSubscribe()
        let id = this._getNextID()
        if (sortID == undefined) {
            sortID = 1
        }
        /* 空数组直接添加 */
        if (this._sortIDList.length == 0) {
            this._addSubscribe(obj, sortID, id)
            return id
        }
        /** 是否已经添加了的标记 */
        let isAdd = false
        /* 大于0,则从后面开始往前靠 */
        if (sortID >= 0) {
            for (let i = this._sortIDList.length - 1; i >= 0; i--) {
                if (sortID >= this._sortIDList[i]) {
                    this._addSubscribe(obj, sortID, id, i + 1)
                    isAdd = true
                    break
                }
            }
            if (!isAdd)
                this._addSubscribe(obj, sortID, id, 0)
        }
        /* 小于0,则从前面开始往后面靠 */
        else {
            for (let i = 0; i < this._sortIDList.length; i++) {
                if (sortID <= this._sortIDList[i]) {
                    this._addSubscribe(obj, sortID, id, i)
                    isAdd = true
                    break
                }
            }
            if (!isAdd)
                this._addSubscribe(obj, sortID, id, this._sortIDList.length)
        }
        return id
    }

    /** 提前订阅观察者事件
     * @method subscribe(obj,-1)
     * @param obj 订阅的事件
     * @returns 返回订阅的id,用来unsubscribe
     */
    advanceSubcribe(obj: (data?: T) => void) {
        return this.subscribe(obj, -1)
    }

    /** 最前订阅观察者事件 
     * @method subscribe(obj,-2)
        * @param obj 订阅的事件
     * @returns 返回订阅的id,用来unsubscribe
     */
    foremostSubcribe(obj: (data?: T) => void) {
        return this.subscribe(obj, -2)
    }

    /** 往后订阅观察者事件 
     * @method subscribe(obj,2)
     * @param obj 订阅的事件
     * @returns 返回订阅的id,用来unsubscribe
     */
    backwardSubcribe(obj: (data?: T) => void) {
        return this.subscribe(obj, 2)
    }

    /** 最后订阅观察者事件 
     * @method subscribe(obj,3)
     * @param obj 订阅的事件
     * @returns 返回订阅的id,用来unsubscribe
     */
    finalySubcribe(obj: (data?: T) => void) {
        return this.subscribe(obj, 3)
    }

    /**
     * 根据排列数值对事件进行删除,同样数值会批量删除
     * @param sortID 默认数值为1
     */
    delBySortID(sortID: number = 1) {
        /* 由于数值是按照大小排列,可以通过比较来减少运算量 */
        /* 大于0,则从后面开始往前靠 */
        if (sortID >= 0) {
            for (let i = this._sortIDList.length - 1; i >= 0; i--) {
                /* 如果比当前值都大,则没有往下算的意义 */
                if (sortID > this._sortIDList[i]) {
                    break
                }
                if (sortID == this._sortIDList[i]) {
                    this._delSubscribe(i)
                }
            }
        }
        /* 小于0,则从前面开始往后面靠 */
        else {
            let len = this._sortIDList.length
            let current = 0
            while (current < len) {
                /* 如果比当前值都小,则没有往下算的意义 */
                if (sortID < this._sortIDList[current]) {
                    break
                }
                /* 删减数组元素,减少数组长度,当前值不变 */
                if (sortID == this._sortIDList[current]) {
                    this._delSubscribe(current)
                    len--
                }
                else {
                    current++
                }
            }
        }
    }

    /**
     * 触发事件
     * @param data 传送的数据
     * @param errCB 错误回调
     */
    emit(data?: T, errCB?: (func: (data?: T) => void) => void) {
        // if (this._subscribers.length == 0) { 
        //     console.error(`事件无订阅`); 
        //     errCB && errCB;
        //     return; 
        // };
        for (let i = 0; i < this._subscribers.length; i++) {
            try {
                this._subscribers[i](data)
            }
            catch (e) {
                // console.error(e)
                console.error(`事件报错${e}`)
                if (errCB)
                    errCB(this._subscribers[i])
            }

        }
    }

    /**
     * 尝试触发事件
     * @param data 传送的数据
     */
    async tryEmit(data?: T) {
        return new Promise<void>((resolve, reject) => {
            for (let i = 0; i < this._subscribers.length; i++) {
                try {
                    this._subscribers[i](data)
                }
                catch (e) {
                    console.error(e)
                    console.error(`事件报错`)
                    reject(this._subscribers[i])
                }
            }
            resolve();
        })
    }

    /**
     * 异步触发事件
     * @param data 传送的数据
     */
    async asyncEmit(data?: T) {
        return new Promise<void>(async (resolve, reject) => {
            for (let i = 0; i < this._subscribers.length; i++) {
                try {
                    await this._subscribers[i](data)
                }
                catch (e) {
                    console.error(e)
                    reject(e)
                }
            }
            resolve();
        })
    }

    /**
     * 取消订阅
     * @param idOrFunc 订阅对应的id或者方法,不写则全取消
     * @returns 取消成功返回true,取消失败返回false
     */
    unsubscribe(idOrFunc?: number | ((data?: T) => void)) {
        /** 触发取消监听订阅的方法 */
        if (this._listenUnsubscribe)
            this._listenUnsubscribe()
        /** 没有值,就全部取消 */
        if (!idOrFunc) {
            this.reset()
            return true
        }
        /* id */
        if (typeof idOrFunc == "number")
            for (let i = this._idList.length - 1; i >= 0; i--) {
                if (this._idList[i] == idOrFunc) {
                    this._delSubscribe(i)
                    return true
                }
            }
        /* 方法 */
        else
            for (let i = this._subscribers.length - 1; i >= 0; i--) {
                if (this._subscribers[i] == idOrFunc) {
                    this._delSubscribe(i)
                    return true
                }
            }
        return false
    }

    /** 重置 */
    reset() {
        this._subscribers = []
        this._sortIDList = []
        this._idList = []
    }
}